package net.sk.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class WSServlet extends WebSocketServlet {
	public static final String ACTION_SUBSCRIBE = "action.subscribe";
	public static final String ACTION_EVENT = "action.event";

	protected Logger log = LoggerFactory.getLogger(this.getClass());
	private static final long serialVersionUID = 1L;
	private static HashMap<String, WSClient> clients = new HashMap<String, WSClient>();
	final AtomicInteger atomicInt = new AtomicInteger(0);
	final String PRE_ID = "ws_";
	private final ScriptEngine engine_out;
	ObjectMapper mapper;

	public WSServlet() {
		ScriptEngineManager manager = new ScriptEngineManager();
		engine_out = manager.getEngineByName("js");
		mapper = new ObjectMapper();
	}

	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest req, String protocol) {
		// TODO Auto-generated method stub
		WSClient w = new WSClient(PRE_ID + atomicInt.incrementAndGet());
		return w;

	}

	public void sendTo(String clientId, String msg) {
		WSClient client = clients.get(clientId);
		client.sendMessage(msg);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void sendAction(String action) {
		StateDTO dto = new StateDTO();
		dto.action = action;
		String msg;
		try {
			msg = mapper.writeValueAsString(dto);
			for (String key : clients.keySet()) {
				WSClient client = clients.get(key);
				client.sendMessage(msg);
			}
		} catch (JsonGenerationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public synchronized void sendData(String action, List data) {
		sendData(action, data.toArray());
	}

	public synchronized void sendData(String action, Object[] data) {
		if ((data == null) || (data.length == 0)) {
			sendAction(action);
			return;
		}
		String cn = data[0].getClass().getName();
		for (Object obj : data) {
			engine_out.put("e", obj);
			for (String key : clients.keySet()) {
				WSClient client = clients.get(key);
				if (!client.bSend(cn, engine_out)) {
					continue;
				}
				client.sendAdd(obj);
			}
		}
		for (String key : clients.keySet()) {
			WSClient client = clients.get(key);
			client.sendFlush(action, cn);
		}
	}

	class WSClient implements OnTextMessage {
		WebSocket.Connection connection;
		// Gson gson;
		ObjectMapper mapper;

		// 订阅的数据类
		private HashMap<String, String> smap = null;
		private String id;
		private List sendbuf;

		public String getId() {
			return id;
		}

		WSClient(String id) {
			this.id = id;
			this.smap = new HashMap<String, String>();
			this.sendbuf = new ArrayList();
			mapper = new ObjectMapper();
		}

		/**
		 * 添加到发送缓冲
		 * 
		 * @param obj
		 */
		public void sendAdd(Object obj) {
			sendbuf.add(obj);
		}

		/**
		 * 将发送缓冲数据送出并清空缓冲
		 * 
		 * @param action
		 * @param cn
		 */
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public synchronized void sendFlush(String action, String cn) {
			if (sendbuf.isEmpty()) {
				return;
			}
			StateDTO dto = new StateDTO();
			dto.action = action;
			dto.cn = cn;
			dto.tm = new Date();
			dto.data = this.sendbuf;
			try {
				sendMessage(mapper.writeValueAsString(dto));
			} catch (JsonGenerationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JsonMappingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sendbuf.clear();
		}

		/**
		 * 订阅指定条件的数据类
		 * 
		 * @param sm
		 */
		@SuppressWarnings("unused")
		private void subscribe(List<ArrayList> sl) {
			for (int i = 0; i < sl.size(); i++) {
				Object[] s = sl.get(i).toArray();
				log.debug(s[0].getClass().getName());
				smap.put(s[0].toString(), s[1].toString());
			}
		}

		/**
		 * 根据订阅条件 判定是否需要发送数据对象
		 * 
		 * @param cn
		 * @param obj
		 * @return
		 * @throws ScriptException
		 */
		public boolean bSend(String cn, ScriptEngine se) {
			if (!smap.containsKey(cn)) {
				return false;
			} else {
				String filter = smap.get(cn);
				if ((filter == null) || filter.trim().equals("")) {
					return true;
				} else {
					try {
						log.info(se.get("e").toString());
						return (Boolean) se.eval(filter);
					} catch (ScriptException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					}
				}
			}
		}

		@Override
		public void onOpen(Connection connect) {
			this.connection = connect;
			clients.put(getId(), this);
			log.debug("open ws:" + this.getId());
			// sendMessage("reload", loadDatas());
		}

		@Override
		public void onClose(int code, String message) {
			clients.remove(this.getId());
			log.debug("close ws:" + this.getId());
		}

		@Override
		public void onMessage(String message) {
			// 处理消息，通知到其他各个客户端
			log.debug(message);
			StateDTO<ArrayList> dto;
			try {
				dto = mapper.readValue(message, StateDTO.class);
				if (dto.action.endsWith(ACTION_SUBSCRIBE)) {
					subscribe(dto.data);
				}
				sendData("action.event", HeartThread.randEvent1(100));
			} catch (JsonMappingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		private void sendMessage(String message) {
			try {
				this.connection.sendMessage(message);
				// 发送错误即可认为对方已退出连接
			} catch (IOException e) {
				e.printStackTrace();
				this.connection.close();
				clients.remove(this.getId());
			}
		}

	}

}
